﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using com.eforceglobal.crux.BusinessObjects;
using com.eforceglobal.crux.configuration.DTOs;
using com.eforceglobal.crux.BusinessObjects.Enums;
using com.eforceglobal.crux.DTOs;
using com.eforceglobal.crux.security.EncryptionAndHashing.Enums;
using com.eforceglobal.crux.BusinessObjects.DatabaseQueries;
using com.eforceglobal.crux.configuration;
using com.eforceglobal.crux.BusinessFacades;

namespace com.eforceglobal.crux.security
{
    public class CruxSecurityManager : ICruxSecurityManager
    {
        
        public bool AuthenticateUser(string UserName, string Password, string CompanyName)
        {
            Queries userQuery = new Queries();
            if (userQuery.GetUser(UserName, Password, CompanyName) != null)
                return true;
            else
                return false;
        }


        public bool AuthenticateUser(string UserName, string Password)
        {
            Queries userQuery = new Queries();
            if (CruxConfigurationManager.GetPasswordStorageType() == PasswordStorageType.hashed)
                Password = Hasher.GetHash(Password);
            if (userQuery.GetUser(UserName, Password) != null) return true;
            return false;
        }

        public UserInformation GetUserFromCompany(string UserName, string CompanyName)
        {
            Queries userQuery = new Queries();
            User user = userQuery.GetUserFromCompany(UserName, CompanyName);
            return ClassConverter.CopyUserToUserInformation(user);
        }
        
        public List<RoleInformation> GetRolesForUser(string UserName, string CompanyName)
        {
            Queries roleQuery = new Queries();
            IEnumerable<RoleInformation> roleInformations = from roles in roleQuery.GetRolesForUser(UserName, CompanyName)
                                                            select ClassConverter.CopyRoleToRoleInformation(roles);
            return roleInformations.ToList();
        }

        public List<RoleInformation> GetRolesForUser(int userid)
        {
            Queries roleQuery = new Queries();
            IEnumerable<RoleInformation> roleInformations = from roles in roleQuery.GetRolesForUser(userid)
                                                            select ClassConverter.CopyRoleToRoleInformation(roles);
            return roleInformations.ToList();
        }


        
        public bool IsUserInRole(string UserName, string CompanyName, string RoleName)
        {
            Queries roleQuery = new Queries();
            IEnumerable<Role> roleInformations = from roles in roleQuery.GetRolesForUser(UserName, CompanyName)
                                                            where roles.RoleName == RoleName
                                                            select roles;
            if (roleInformations.Count() > 0) return true;
                return false;   
        }

        public bool IsUserInRole(string UserName, string CompanyName, List<string> RoleNames)
        {
            Queries roleQuery = new Queries();
            IEnumerable<Role> roleInformations = from roles in roleQuery.GetRolesForUser(UserName, CompanyName)
                                                 .Where(p=>RoleNames.Contains(p.RoleName))
                                                 select roles;
            if (roleInformations.Count() > 0) return true;
            return false;               
        }
        
        public bool HasAccessToView(string UserName, string CompanyName, string ViewName)
        {
            ViewInformation viewInformation = CruxConfigurationManager.GetViewInformation(ViewName);
            RoleConfigurationInformation roleConfiguration = viewInformation.RoleConfigurationInformation;
            return HasAccessAsPerRoleConfiguration(UserName, CompanyName, roleConfiguration);
        }
        
        public List<NavigationInformation> GetNavigationItemsForUser(string UserName, string CompanyName)
        {
            List<NavigationInformation> navigationInformations = CruxConfigurationManager.GetAllNavigationItems();
            List<NavigationInformation> returnNavigationItems = navigationInformations;
            
            // Reverse Loop So That Items Can Be Removed From The Same Collection Within The Loop As It Iterates. (rpopat).
            for (int i = navigationInformations.Count - 1; i >= 0; i--)
            {
                if (!HasAccessToNavigationItem(UserName, CompanyName, navigationInformations[i].NaviagtionItemName))
                    navigationInformations.RemoveAt(i);
            }

            return navigationInformations.OrderBy(p => p.NavigationItemSortOrder).ToList();
        }

        private bool HasAccessToNavigationItem(string UserName, string CompanyName, string NavigationItemName)
        {
            NavigationInformation naviagationInformation =
               CruxConfigurationManager.GetNavigationInformation(NavigationItemName);
            RoleConfigurationInformation roleConfiguration = naviagationInformation.RoleConfigurationInformation;
            return HasAccessAsPerRoleConfiguration(UserName, CompanyName, roleConfiguration);
        }
        
        private bool HasAccessAsPerRoleConfiguration(string UserName, string CompanyName, RoleConfigurationInformation roleConfiguration)
        {
            if (roleConfiguration.RoleCondition == Conditions.all)
                return true;
            else if (roleConfiguration.RoleCondition == Conditions.or)
                return HasOrAccess(UserName, CompanyName, roleConfiguration);
            else if (roleConfiguration.RoleCondition == Conditions.and)
                return HasAndAccess(UserName, CompanyName, roleConfiguration);
            else if (roleConfiguration.RoleCondition == Conditions.single)
                return HasSingleAccess(UserName, CompanyName, roleConfiguration);
            else
                throw CruxSecurityExceptions.InvalidConditionException;
        }
        
        private bool HasOrAccess(string UserName, string CompanyName, RoleConfigurationInformation roleConfiguration)
        {
            List<RoleInformation> userRoles = GetRolesForUser(UserName, CompanyName);
            if (roleConfiguration.RoleCondition != Conditions.or) throw CruxSecurityExceptions.InvalidConditionException;
            foreach (string RoleName in roleConfiguration.RoleNames)
            {
                if (userRoles.SingleOrDefault(p => p.RoleName == RoleName) != null)
                    return true;
            }
            return false;
        }
        
        private bool HasAndAccess(string UserName, string CompanyName, RoleConfigurationInformation roleConfiguration)
        {
            List<RoleInformation> userRoles = GetRolesForUser(UserName, CompanyName);
            if (roleConfiguration.RoleCondition != Conditions.and) throw CruxSecurityExceptions.InvalidConditionException;
            foreach (string RoleName in roleConfiguration.RoleNames)
            {
                if (userRoles.SingleOrDefault(p => p.RoleName == RoleName) == null)
                    return false;
            }
            return true;
        }

        private bool HasSingleAccess(string UserName, string CompanyName, RoleConfigurationInformation roleConfiguration)
        {
            List<RoleInformation> userRoles = GetRolesForUser(UserName, CompanyName);
            
            //TODO: Give formal Names To These Exceptions And Log them.
            if (roleConfiguration.RoleCondition != Conditions.single) throw CruxSecurityExceptions.InvalidConditionException;
            if (roleConfiguration.RoleNames.Count > 1) throw CruxSecurityExceptions.InvalidConditionException;
            if (userRoles.SingleOrDefault(p => p.RoleName == roleConfiguration.RoleNames.First()) == null)
                    return false;
            return true;

        }

        public CompanyInformation GetCompayByUser(string UserName)
        {
            Queries query = new Queries();
            CompanyInformation company = query.GetCompanyForUser(UserName);
            return company;
        }

        public List<UserInformation> GetUsersByRole(List<string> RoleNames)
        {
            Queries query = new Queries();
            IEnumerable<UserInformation> usersinroles = 
                                        from usersinrole in query.GetUsersInRoles(RoleNames)
                                        select ClassConverter.CopyUserToUserInformation
                                        (usersinrole);
            return usersinroles.ToList(); 
        }

        public string GetApplicationPath()
        {
            return CruxConfigurationManager.GetApplicationPath();
        }

        public bool IsUserSuperAdministrator(UserInformation userInformation)
        {
            RoleFacade roleFacade = new RoleFacade();
            List<RoleInformation> roles = roleFacade.GetSuperAdministativeRoles();
            foreach (RoleInformation role in roles)
            {
                if (IsUserInRole(userInformation.UserName, userInformation.CompanyName, role.RoleName))
                    return true;
            }
            return false;
        }


        public bool IsUserAdministrator(UserInformation userInformation)
        {
            RoleFacade roleFacade = new RoleFacade();
            List<RoleInformation> roles = roleFacade.GetAdministrativeRoles();
            foreach (RoleInformation role in roles)
            {
                if (IsUserInRole(userInformation.UserName, userInformation.CompanyName, role.RoleName))
                    return true;
            }
            return false;
        }

        public bool IsUserSuperAdminOrCompanyAdmin(UserInformation CurrentUser, int CompanyID)
        {
            bool isUserAdministrator = IsUserAdministrator(CurrentUser);
            bool isUserSuperAdministrator = IsUserSuperAdministrator(CurrentUser);
            bool doesUserBelongToCompany = CurrentUser.CompanyID == CompanyID;
            bool isUserAdministratorOfCompany = isUserAdministrator && doesUserBelongToCompany;
            if (isUserSuperAdministrator || isUserAdministratorOfCompany)
                return true;
            return false;
        }

        public bool IsUserAuthorizedToEditUser(UserInformation CurrentUser, UserInformation UserBeingModified)
        {
            bool isCurrentUserSuperAdmin = IsUserSuperAdministrator(CurrentUser);
            bool isUserBeingModifiedSuperAdmin = IsUserSuperAdministrator(UserBeingModified);
            if (!isCurrentUserSuperAdmin && isUserBeingModifiedSuperAdmin)
                return false;
            return true;
        }

        public bool IsUserAuthorizedToChangePassword(UserInformation CurrentUser, UserInformation UserBeingModified)
        {
            bool isUserSuperAdminOrCompanyAdmin = IsUserSuperAdminOrCompanyAdmin(CurrentUser, UserBeingModified.CompanyID);
            bool isUserAuthorizedToEditUser = IsUserAuthorizedToEditUser(CurrentUser, UserBeingModified);
            if (isUserSuperAdminOrCompanyAdmin && isUserAuthorizedToEditUser)
                return true;
            else if (CurrentUser.UserID == UserBeingModified.UserID)
                return true;
            else
                return false;
        }

        public List<string> GetAllRoles(int CompanyID)
        {
            RoleFacade roleFacade = new RoleFacade();
            List<string> roles = roleFacade.GetAllRoles(CompanyID).Select(p => p.RoleName).ToList();
            return roles;
        }

        public List<string> GetRolesByPrivilage(string PrivilageName)
        {
            RoleFacade roleFacade = new RoleFacade();
            List<string> roles = roleFacade.GetRoleNamesByPrivilage(PrivilageName);
            return roles;
        }


        public bool DoesUserHavePrivilage(UserInformation user, string PrivilageName)
        {
           List<string> roles = GetRolesByPrivilage(PrivilageName);
            ICruxSecurityManager cruxSecurityManager = CruxSecurityManagerFactory.GetSecurityManager();
            bool isUserInRole = cruxSecurityManager
                .IsUserInRole(user.UserName, user.CompanyName, roles);
            return isUserInRole;
        }

        public List<UserInformation> GetUsersByPrivilage(string PrivilageName)
        {
           List<string> roles = GetRolesByPrivilage(PrivilageName);
           List<UserInformation> users = GetUsersByRole(roles);
           return users;
        }
    }
}
